package com.android.common.asyntask;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.util.Log;

import com.android.common.util.DateUtil;

/**
 * <pre>
 * 1.异步任务处理类，使用方法和AsyncTask相同。
 *   增加了超时控制和任务优先级设置，
 *   超时默认3s，如执行长时间任务，请重载getTimeOut方法，返回LONG_TIME_OUT，使用NOT_TIME_OUT（不建议）则无超时。
 *   优先级默认都是低优先级，启动高优先级任务请继承HighAsyncTask。
 * 
 * 2.需要将一个任务的生命周期和context绑定，详情请看基类BindContextAsyncTask
 *   a.初始化activity视图任务（基类LoadViewAsyncTask）：请继承每个父类（如BaseAactivity）中的InitAsyncTask类。
 *     会在任务开始自动弹出进度框，结束消失进度框，返回空时显示空页面 。
 *   b.执行操作(如收藏，加关注等操作)请继承OperAsyncTask
 * 
 * </pre>
 * 
 * @param <Params>
 * @param <Progress>
 * @param <Result>
 */
public abstract class AsyncTask<Params, Progress, Result> {
	protected static final String TAG = "AsyncTask";

	private static final int KEEP_ALIVE = 1;

	private static final long DEFAULT_TIME_OUT = 12 * 1000;// 超时时间

	public static final long NORMAL_TIME_OUT = DEFAULT_TIME_OUT * 2;// 普通的加长时间任务

	public static final long LONG_TIME_OUT = DateUtil.FIVE_MIN;// 超长时间的超时

	public static final long NOT_TIME_OUT = 0;// 没有超时时间

	/**
	 * 高优先级线程池 配置
	 */
	private static final int HIGH_CORE_POOL_SIZE = 4;
	private static final int HIGH_MAXIMUM_POOL_SIZE = 64;
	private static final ThreadFactory sHighThreadFactory = new ThreadFactory() {
		private final AtomicInteger mCount = new AtomicInteger(1);

		public Thread newThread(Runnable r) {
			return new Thread(r, "HighAsyncTask #" + mCount.getAndIncrement());
		}
	};

	private static final BlockingQueue<Runnable> sHighPoolWorkQueue = new LinkedBlockingQueue<Runnable>(
			HIGH_CORE_POOL_SIZE * 2);

	public static final ThreadPoolExecutor HIGH_EXECUTOR = new ThreadPoolExecutor(
			HIGH_CORE_POOL_SIZE, HIGH_MAXIMUM_POOL_SIZE, KEEP_ALIVE,
			TimeUnit.SECONDS, sHighPoolWorkQueue, sHighThreadFactory,
			new ThreadPoolExecutor.DiscardOldestPolicy());

	/**
	 * 低优先级线程池 配置
	 */
	private static final int LOW_CORE_POOL_SIZE = 2;
	private static final int LOW_MAXIMUM_POOL_SIZE = 32;

	private static final ThreadFactory sLowThreadFactory = new ThreadFactory() {
		private final AtomicInteger mCount = new AtomicInteger(1);

		public Thread newThread(Runnable r) {
			return new Thread(r, "LowAsyncTask #" + mCount.getAndIncrement());
		}
	};

	private static final BlockingQueue<Runnable> sLowPoolWorkQueue = new LinkedBlockingQueue<Runnable>(
			LOW_CORE_POOL_SIZE * 2);

	public static final ThreadPoolExecutor LOW_EXECUTOR = new ThreadPoolExecutor(
			LOW_CORE_POOL_SIZE, LOW_MAXIMUM_POOL_SIZE, KEEP_ALIVE,
			TimeUnit.SECONDS, sLowPoolWorkQueue, sLowThreadFactory,
			new ThreadPoolExecutor.DiscardOldestPolicy());

	private static final int MESSAGE_POST_RESULT = 0x1;
	private static final int MESSAGE_POST_PROGRESS = 0x2;

	private static final InternalHandler sHandler = new InternalHandler();

	protected final WorkerRunnable<Params, Result> mWorker;
	private final FutureTask<Result> mFuture;

	private volatile Status mStatus = Status.PENDING;

	private String canceReason;

	private static final Timer timeOutListener = new Timer(true);

	private TimerTask timeOutTask;

	private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

	private final AtomicBoolean canceled = new AtomicBoolean();

	public enum Status {
		/**
		 * Indicates that the task has not been executed yet.
		 */
		PENDING,
		/**
		 * Indicates that the task is running.
		 */
		RUNNING,
		/**
		 * Indicates that {@link AsyncTask#onPostExecute} has finished.
		 */
		FINISHED,
	}

	public enum Priority {
		LOW, HIGH,
	}

	public Priority getPriority() {
		return Priority.LOW;
	}

	/** @hide Used to force static handler to be created. */
	public static void init() {
		sHandler.getLooper();
	}

	public AsyncTask() {
		mWorker = new WorkerRunnable<Params, Result>() {
			public Result call() throws Exception {
				mTaskInvoked.set(true);
				Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

				final long timeOut = getTimeOut();
				if (timeOut <= NOT_TIME_OUT || timeOut > LONG_TIME_OUT) {// 超长时间的任务或者无超时的任务，警告！！！
					Log.w(TAG, AsyncTask.this.getClass()
							+ " time out is too long!!! value:" + timeOut);
				}
				if (timeOut > NOT_TIME_OUT) {// 设定超时时间
					timeOutTask = new TimerTask() {
						@Override
						public void run() {
							if (!isFinished()) {
								AsyncTask.this
										.cancel(true, "task run timeout!");
							}
						}
					};
					timeOutListener.schedule(timeOutTask, timeOut);
				}

				Result result = null;
				try {
					result = doInBackground(mParams);
				} catch (Exception e) {
					Log.e(TAG, "doInBackground error:", e);
				}
				return postResult(result);
			}
		};

		mFuture = new FutureTask<Result>(mWorker) {
			@Override
			protected void done() {
				try {
					final Result result = get();
					postResultIfNotInvoked(result);
				} catch (InterruptedException e) {
					android.util.Log.w(TAG, e);
				} catch (ExecutionException e) {
					throw new RuntimeException(
							"An error occured while executing doInBackground()",
							e.getCause());
				} catch (CancellationException e) {
					postResultIfNotInvoked(null);
				} catch (Throwable t) {
					throw new RuntimeException(
							"An error occured while executing "
									+ "doInBackground()", t);
				}
			}
		};
	}

	private void postResultIfNotInvoked(Result result) {
		final boolean wasTaskInvoked = mTaskInvoked.get();
		Log.d(TAG, "asynTask postResultIfNotInvoked:" + wasTaskInvoked);
		if (!wasTaskInvoked) {
			postResult(result);
		}
	}

	@SuppressWarnings("unchecked")
	private Result postResult(Result result) {
		Log.d(TAG, "AsyncTask postResult");
		Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
				new AsyncTaskResult<Result>(this, result));
		message.sendToTarget();
		return result;
	}

	public final Status getStatus() {
		return mStatus;
	}

	protected abstract Result doInBackground(Params... params);

	protected void onPreExecute() {
	}

	protected void onFinishExecute(Result result) {
		Log.i(TAG, "onPostExecute");
	}

	protected void onProgressUpdate(Progress... values) {
	}

	protected void onCancelled(Result result) {
		onCancelled();
	}

	protected void onCancelled() {
		Log.d(TAG, getClass() + " AsynTask canceled:" + canceReason);
	}

	public String getCanceReason() {
		return canceReason;
	}

	public final boolean isCancelled() {
		return canceled.get() || mFuture.isCancelled();
	}

	public final boolean isFinished() {
		return mStatus == Status.FINISHED;
	}

	public final boolean cancel(boolean mayInterruptIfRunning) {
		return cancel(mayInterruptIfRunning, "Please record cancel reason!!!");
	}

	/**
	 * FutureTask 的cancel并不是一定可靠的，所以直接更改标记，并且发送Message返回主线程
	 * 
	 * @param mayInterruptIfRunning
	 * @param msg
	 * @return
	 */
	public final boolean cancel(boolean mayInterruptIfRunning, String msg) {
		canceReason = msg;
		canceled.set(true);
		postResult(null);
		return mFuture.cancel(mayInterruptIfRunning);
	}

	public final Result get() throws InterruptedException, ExecutionException {
		return mFuture.get();
	}

	public final Result get(long timeout, TimeUnit unit)
			throws InterruptedException, ExecutionException, TimeoutException {
		return mFuture.get(timeout, unit);
	}

	public final AsyncTask<Params, Progress, Result> execute(Params... params) {
		return executeOnExecutor(getPriority() == Priority.HIGH ? HIGH_EXECUTOR
				: LOW_EXECUTOR, params);
	}

	/**
	 * 获取超时时间，无超时时间请返回NOT_TIME_OUT
	 * 
	 * @return
	 */
	public long getTimeOut() {
		return DEFAULT_TIME_OUT;
	}

	public final AsyncTask<Params, Progress, Result> executeOnExecutor(
			Executor exec, Params... params) {
		if (mStatus != Status.PENDING) {
			switch (mStatus) {
			case RUNNING:
				throw new IllegalStateException("Cannot execute task:"
						+ " the task is already running.");
			case FINISHED:
				throw new IllegalStateException("Cannot execute task:"
						+ " the task has already been executed "
						+ "(a task can be executed only once)");
			}
		}

		mStatus = Status.RUNNING;

		onPreExecute();

		mWorker.mParams = params;
		exec.execute(mFuture);

		return this;
	}

	protected final void publishProgress(Progress... values) {
		if (!isCancelled()) {
			sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
					new AsyncTaskResult<Progress>(this, values)).sendToTarget();
		}
	}

	private void finish(Result result) {
		mStatus = Status.FINISHED;
		if (timeOutTask != null) {
			timeOutTask.cancel();
		}
		try {
			if (isCancelled()) {
				onCancelled(result);
			} else {
				onFinishExecute(result);
			}
		} catch (Exception e) {
			Log.e(TAG, "TaobanAsyncTask finish error:", e);
		} finally {
			onComplete(result);
		}
	}

	/**
	 * 一定会执行的，不管成功以否
	 * 
	 * @param result
	 */
	protected void onComplete(Result result) {

	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static class InternalHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			AsyncTaskResult result = (AsyncTaskResult) msg.obj;
			Log.d(TAG, "AsyncTask handleMessage what:" + msg.what);
			switch (msg.what) {
			case MESSAGE_POST_RESULT:
				result.mTask.finish(result.mData[0]);
				break;
			case MESSAGE_POST_PROGRESS:
				result.mTask.onProgressUpdate(result.mData);
				break;
			}
		}
	}

	private static abstract class WorkerRunnable<Params, Result> implements
			Callable<Result> {
		Params[] mParams;
	}

	@SuppressWarnings("rawtypes")
	private static class AsyncTaskResult<Data> {
		final AsyncTask mTask;
		final Data[] mData;

		AsyncTaskResult(AsyncTask task, Data... data) {
			mTask = task;
			mData = data;
		}
	}

	public static void destoryPool() {
		try {
			HIGH_EXECUTOR.shutdownNow();
			LOW_EXECUTOR.shutdownNow();
			timeOutListener.cancel();
		} catch (Exception e) {
			Log.e(TAG, "pool destory error:", e);
		}
	}

	@SuppressWarnings("unchecked")
	public void reStart() {
		this.mStatus = Status.PENDING;
		try {
			((AsyncTask<Params, Progress, Result>) clone())
					.execute(mWorker.mParams);
		} catch (CloneNotSupportedException e) {
			Log.d(TAG, "reStart error:", e);
		}
	}

	protected Params[] getParams() {
		return mWorker.mParams;
	}

	public void setParams(Params... params) {
		mWorker.mParams = params;
	}
}
